-- package.cpath = "luaclib/?.so"

-- local socket = require "client.socket"
-- local crypt = require "client.crypt"

-- -- if _VERSION ~= "Lua 5.4.8" then
-- --     error "Use lua 5.4.8"
-- -- end


-- local fd = assert(socket.connect("127.0.0.1", 8001))

-- local function writeline(fd, text)
--     socket.send(fd, text .. "\n")
-- end

-- local function unpack_line(text)
--     local from = text:find("\n", 1, true)
--     if from then
--         return text:sub(1, from-1), text:sub(from+1)
--     end
--     return nil, text
-- end

-- local last = ""

-- local function unpack_f(f)
--     local function try_recv(fd, last)
--         local result
--         result, last = f(last)
--         if result then
--             return result, last
--         end
--         local r = socket.recv(fd)
--         if not r then
--             return nil, last
--         end
--         if r == "" then
--             error "Server closed"
--         end
--         return f(last .. r)
--     end

--     return function()
--         while true do
--             local result
--             result, last = try_recv(fd, last)
--             if result then
--                 return result
--             end
--             socket.usleep(100)
--         end
--     end
-- end

-- local readline = unpack_f(unpack_line)

-- local challenge = crypt.base64decode(readline()) --接收challenge

-- local clientkey = crypt.randomkey()
-- --把clientkey换算后比如称它为ckeys，发给服务器
-- writeline(fd, crypt.base64encode(crypt.dhexchange(clientkey))) 
-- --服务器也把serverkey换算后比如称它为skeys，发给客户端，客户端用clientkey与skeys所出secret
-- local secret = crypt.dhsecret(crypt.base64decode(readline()), clientkey) 
-- --secret一般是8字节数据流，需要转换成16字节的hex字符串来显示。
-- print("sceret is ", crypt.hexencode(secret)) 
-- --加密的时候还是需要直接传递secret字节流
-- -- local hmac = crypt.hmac64("1147C34D", secret)  
-- local hmac = crypt.hmac64(challenge, secret)  
-- writeline(fd, crypt.base64encode(hmac))      

-- local token = {
--     server = "sample",
--     user = "hello",
--     pass = "password",
-- }

-- local function encode_token(token)
--     return string.format("%s@%s:%s",
--         crypt.base64encode(token.user),
--         crypt.base64encode(token.server),
--         crypt.base64encode(token.pass))
-- end
-- --使用DES加密token得到etoken, etoken是字节流
-- local etoken = crypt.desencode(secret, encode_token(token)) 
-- etoken = crypt.base64encode(etoken)
-- --发送etoken，myloginserver.lua将会调用auth_handler回调函数, 以及login_handler回调函数。
-- writeline(fd, etoken) 

-- local result = readline() --读取最终的返回结果。
-- print("result", result)
-- local code = tonumber(string.sub(result, 1, 3))
-- assert(code == 200)
-- socket.close(fd)  --可以关闭链接了

-- local subid = crypt.base64decode(string.sub(result, 5)) --解析出subid

-- print("login ok, subid=", subid)



-- ----- connect to gate server 新增内容，以下通信协议全是两字节数据长度协议。

-- local function send_request(v, session) --打包数据v以及session
--     local size = #v + 4
--     -->I2大端序2字节unsigned int，>I4大端序4字节unsigned int
--     local package = string.pack(">I2", size)..v..string.pack(">I4", session)
--     socket.send(fd, package)
--     return v, session
-- end

-- local function recv_response(v)--解包数据v得到content（内容）、ok（是否成功）、session（会话序号）
--     local size = #v - 5
--     --cn：n字节字符串 ; B>I4: B unsigned char，>I4，大端序4字节unsigned int
--     local content, ok, session = string.unpack("c"..tostring(size).."B>I4", v)
--     return ok ~=0 , content, session
-- end

-- local function unpack_package(text)--读取两字节数据长度的包
--     local size = #text
--     if size < 2 then
--         return nil, text
--     end
--     local s = text:byte(1) * 256 + text:byte(2)
--     if size < s+2 then
--         return nil, text
--     end

--     return text:sub(3,2+s), text:sub(3+s)
-- end

-- local readpackage = unpack_f(unpack_package)

-- local function send_package(fd, pack)
--     local package = string.pack(">s2", pack)  -->大端序，s计算字符串长度，2字节整形表示
--     socket.send(fd, package)
-- end

-- local text = "echo"
-- local index = 1

-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002)) --连接登录点对应的ip端口
-- last = ""

-- local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index) --index用于断链恢复
-- -- local handshake = string.format("%s@%s#%s:%d", crypt.base64encode("username"), crypt.base64encode(token.server),crypt.base64encode(subid) , index) --token.use改成了username
-- local hmac = crypt.hmac64(crypt.hashkey(handshake), secret) --加密握手hash值得到hmac，保证handshake数据接收无误，没被篡改。
-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) --发送handshake

-- print(readpackage()) --接收应答
-- print("===>",send_request(text,0)) --发送请求，并同时将当前的session 0组合发送，session用于匹配应答
-- -- print("<===",recv_response(readpackage()))
-- print("disconnect")
-- socket.close(fd)



-- --在末尾添加这几行代码，即使用相同的handshake（index）不变的情况下，再次尝试登录连接。
-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002 )) --连接登录点对应的ip端口
-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) --发送handshake

-- print(readpackage()) --接收应答
-- print("===>",send_request(text,0))
-- print("<===",recv_response(readpackage()))
-- print("disconnect")
-- socket.close(fd)


-- sceret is       4388c8b14c55dffd
-- result  200 NA==
-- login ok, subid=        4
-- connect
-- 200 OK
-- ===>    echo    0
-- <===    true    ECHO    0
-- disconnect
-- connect
-- 403 Index Expired   -- index过期
-- ===>    echo    0
-- ./3rd/lua/lua: ./my_workspace/myclient.lua:39: Server closed
-- stack traceback:
--         [C]: in function 'error'
--         ./my_workspace/myclient.lua:39: in upvalue 'try_recv'
--         ./my_workspace/myclient.lua:47: in local 'readpackage'
--         ./my_workspace/myclient.lua:166: in main chunk
--         [C]: in ?

-- 403状态码表示index已经过期了，index主要用于防止他们恶意使用handshake来登录，
-- handshake使用后一次必须累加index，例如登录完成后index为1，断线重连，这个时候index=2。
-- 如果还是使用之前的index=1
-- 那么就会直接返回403.状态码



--第二次连接
-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002 )) --连接登录点对应的ip端口
-- index = index + 1       --index加一
-- handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index) --重新计算handshake
-- hmac = crypt.hmac64(crypt.hashkey(handshake), secret) --
-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) --发送handshake

-- print(readpackage()) --接收应答
-- print("===>",send_request(text,0))
-- print("<===",recv_response(readpackage()))
-- print("disconnect")
-- socket.close(fd)


-- sceret is       1f945cc98639da0c
-- result  200 NQ==
-- login ok, subid=        5
-- connect
-- 200 OK  -- ok
-- ===>    echo    0
-- <===    true    ECHO    0
-- disconnect
-- connect
-- 200 OK  -- ok
-- ===>    echo    0
-- <===    true    ECHO    0
-- disconnect


-- 请求与应答

-- 请求包发送给msgserver的，但是除了遵循两字节数据长度协议外，数据内容还需要遵循以下规则:

--  len                 request           session
--   |                    |                |
-- 两字节长度           请求内容          四字节sessionID
-- ​由于需要msgserver的请求应答并不需要同步，可以是多个请求一起发送，不用等上一个应答到了，才请求下一个，为了把请求与应答对应起来，就需要添加一个sessionID。整个数据包如下：

-- --发送"12345" sessionID为1、组合好的数据包如下：
-- ​
-- \x00\x09 \x31\x32\x33\x34\x35 \x00\x00\x00\x01
-- 应答包收到后需要解析，需要遵循以下规则来解析：

--  len                 response      ok           session
--   |                    |            |               |
-- 两字节长度           响应内容     一字节状态值     四字节sessionID
-- 例如：

-- --应答返回"12345"
-- \x00\x0a \x31\x32\x33\x34\x35 \x01 \x00\x00\x00\x01


--断线重连
-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002))
-- last = ""
-- index = index + 1
-- local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index)
-- local hmac = crypt.hmac64(crypt.hashkey(handshake), secret)

-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac))
-- print(readpackage())

-- print("===>",send_request("fake",0))    --伪装session0请求，再发送出去一次，发送内容可以随便填
-- print("===>",send_request("again",1))   --发送请求again，session+1。
-- print("<===",recv_response(readpackage()))
-- print("<===",recv_response(readpackage()))

-- print("disconnect")
-- socket.close(fd)

-- 上面可以看到，想得到以后一次的响应，就把任意的请求内容和最后一次的对应的session组合再发送一次。


--  获取历史应答
-- 只要是对应的session已经发送过了，就能获取到响应。
-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002 )) --连接登录点对应的ip端口
-- last = ""

-- local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index) --index用于断链恢复
-- local hmac = crypt.hmac64(crypt.hashkey(handshake), secret) --加密握手hash值得到hmac，最要是保证handshake数据接收无误
-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) --发送handshake

-- print(readpackage()) --接收应答
-- print("===>",send_request(text,0)) --发送两次
-- print("===>",send_request(text,1))
-- --print("<===",recv_response(readpackage())) --不管是否已经接受了
-- --print("<===",recv_response(readpackage()))
-- print("disconnect")
-- socket.close(fd)



-- print("connect")
-- fd = assert(socket.connect("127.0.0.1", 8002))
-- last = ""
-- index = index + 1
-- handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index)
-- hmac = crypt.hmac64(crypt.hashkey(handshake), secret)

-- send_package(fd, handshake .. ":" .. crypt.base64encode(hmac))

-- print(readpackage())
-- print("===>",send_request("fake",0))    -- request again (use last session 0, so the request message is fake)
-- print("===>",send_request("again",1))   -- request again (use new session)
-- print("<===",recv_response(readpackage()))  
-- print("<===",recv_response(readpackage()))


-- print("disconnect")
-- socket.close(fd)


package.cpath = "luaclib/?.so"

local socket = require "client.socket"
local crypt = require "client.crypt"

-- if _VERSION ~= "Lua 5.3" then
--     error "Use lua 5.3"
-- end

local fd = assert(socket.connect("127.0.0.1", 8001))

local function writeline(fd, text)
    socket.send(fd, text .. "\n")
end

local function unpack_line(text)
    local from = text:find("\n", 1, true)
    if from then
        return text:sub(1, from-1), text:sub(from+1)
    end
    return nil, text
end

local last = ""

local function unpack_f(f)
    local function try_recv(fd, last)
        local result
        result, last = f(last)
        if result then
            return result, last
        end
        local r = socket.recv(fd)
        if not r then
            return nil, last
        end
        if r == "" then
            error "Server closed"
        end
        return f(last .. r)
    end

    return function()
        while true do
            local result
            result, last = try_recv(fd, last)
            if result then
                return result
            end
            socket.usleep(100)
        end
    end
end

local readline = unpack_f(unpack_line)

local challenge = crypt.base64decode(readline()) --接收challenge

local clientkey = crypt.randomkey()
writeline(fd, crypt.base64encode(crypt.dhexchange(clientkey))) --把clientkey换算后比如称它为ckeys，发给服务器
local secret = crypt.dhsecret(crypt.base64decode(readline()), clientkey) --服务器也把serverkey换算后比如称它为skeys，发给客户端，客户端用clientkey与skeys所出secret

print("sceret is ", crypt.hexencode(secret)) --secret一般是8字节数据流，需要转换成16字节的hex字符串来显示。

local hmac = crypt.hmac64(challenge, secret) --加密的时候还是需要直接传递secret字节流
writeline(fd, crypt.base64encode(hmac))      

local token = {
    server = "sample",
    user = "nzhsoft",
    pass = "password",
}

local function encode_token(token)
    return string.format("%s@%s:%s",
        crypt.base64encode(token.user),
        crypt.base64encode(token.server),
        crypt.base64encode(token.pass))
end

local etoken = crypt.desencode(secret, encode_token(token)) --使用DES加密token得到etoken, etoken是字节流
writeline(fd, crypt.base64encode(etoken)) --发送etoken，mylogin.lua将会调用auth_handler回调函数, 以及login_handler回调函数。

local result = readline() --读取最终的返回结果。
print(result)
local code = tonumber(string.sub(result, 1, 3))
assert(code == 200)
socket.close(fd)  --可以关闭链接了

local subid = crypt.base64decode(string.sub(result, 5)) --解析出subid

print("login ok, subid=", subid)


----- connect to game server 新增内容，以下通信协议全是两字节数据长度协议。

local function send_request(v, session) --打包数据v以及session
    local size = #v + 4
    local package = string.pack(">I2", size)..v..string.pack(">I4", session)
    socket.send(fd, package)
    return v, session
end

local function recv_response(v)--解包数据v得到content（内容）、ok（是否成功）、session（会话序号）
    local size = #v - 5
    local content, ok, session = string.unpack("c"..tostring(size).."B>I4", v)
    return ok ~=0 , content, session
end

local function unpack_package(text)--解析两字节数据长度协议包
    local size = #text
    if size < 2 then
        return nil, text
    end
    local s = text:byte(1) * 256 + text:byte(2)
    if size < s+2 then
        return nil, text
    end

    return text:sub(3,2+s), text:sub(3+s)
end

local readpackage = unpack_f(unpack_package)

local function send_package(fd, pack)
    local package = string.pack(">s2", pack)  
    socket.send(fd, package)
end

local text = "echo"
local index = 1
local session = 0

print("connect")
fd = assert(socket.connect("127.0.0.1", 8002 )) --连接登录点对应的ip端口
last = ""

local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index) --index用于断链恢复
local hmac = crypt.hmac64(crypt.hashkey(handshake), secret) --加密握手hash值得到hmac，最要是保证handshake数据接收无误
send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) --发送handshake

print(readpackage()) --接收应答

while(true) do   
    text = socket.readstdin() --循环读取标准数据发送给服务器
    if text then
        print("===>",send_request(text,session))
        print("<===",recv_response(readpackage()))
        session = session + 1 --会话ID自动递增
    end
    socket.usleep(100)
end
print("disconnect")
socket.close(fd)

